import copy
import random

import numpy as np

import Super
import Target
import archiving
import load
import config
import update

particals = config.particals  # 粒子数量
MaxIter = config.MaxIter  # 最大迭代次数
time_slot = config.num_days * config.periods_per_day
c1 = c2 = 2
w_min = 0.4
w_max = 0.9

'''加入单双周和起始周'''

'''多目标粒子群优化算法  SPF  2024.12.20 '''


def MOPSO(task_list, students, teachers, classroom_list, buildings_dis):
    x = []
    y1 = []
    y2 = []
    y3 = []

    # 随机初始化粒子位置
    schemesList, RTmatrix_filled, STmatrix_filled, TTmatrix_filled = load.Load(task_list, students, teachers,
                                                                               classroom_list, particals)
    # 复制种群
    copy_schemesList = copy.deepcopy(schemesList)

    # 计算每个粒子适应度
    fitness = Target.target_fun(RTmatrix_filled, STmatrix_filled, TTmatrix_filled, buildings_dis, len(students),
                                len(teachers))

    # 多目标问题pareto解集
    newParetofrontiers = Super.super(fitness)

    # 初始化个体最优，初始是个体最优就是粒子本身
    particle_p, RTparticle_p, fitness_p, s_t_p, t_t_p = init_pbest(copy_schemesList, RTmatrix_filled, fitness,
                                                                   STmatrix_filled, TTmatrix_filled)
    # 初始化速度都为0
    vel = [[[[0, 0] for i in range(16)] for j in range(len(task_list))] for k in range(particals)]

    # 快速非支配排序和拥挤度计算，确定粒子群中的全局最优位置
    particle_g, fitness_g, RTparticle_g = archiving.init_archive(copy_schemesList, newParetofrontiers, RTmatrix_filled)

    # 进行迭代,位置更新，然后计算适应度，更新全局最优
    for t in range(MaxIter):
        update_RTmatrix = []  # 存储此次迭代过程中修改位置后的矩阵列表
        update_schemeslist = []  # 存储迭代过程中修改课程信息后方案的列表
        update_STmatrix_filled = []
        update_TTmatrix_filled = []

        w = w_max + (w_min - w_max) * t / MaxIter

        for i in range(particals):
            # 原有列表不变，进行深拷贝
            matrix_a = copy.deepcopy(RTmatrix_filled[i])
            update_ST = copy.deepcopy(STmatrix_filled[i])
            update_TT = copy.deepcopy(TTmatrix_filled[i])
            update_scheme = copy.deepcopy(copy_schemesList[i])

            # 参数设置
            r1 = random.uniform(0, 1)
            r2 = random.uniform(0, 1)

            # 遍历所有教学任务
            for index, j in enumerate(particle_p[i]):
                task_num = j[0]
                stus = j[6]
                tea = j[3]
                start_week = j[7]
                end_week = j[8]
                week_type = j[11]
                # 在矩阵a中找到task_num教学任务的位置，位置存储在positions_a,找开始周的教室时间片,位置有多个
                positions_a = find_all_element_positions(matrix_a, task_num, start_week)
                positions_p = find_all_element_positions(RTparticle_p[i], task_num, start_week)
                # 在矩阵RTparticle_g中找到task_num教学任务的位置，位置存储在positions_g，找开始周的教室时间片,位置有多个
                positions_g = find_all_element_positions(RTparticle_g, task_num, start_week)
                if positions_a == positions_g == positions_p:
                    continue
                count = len(positions_g)
                for k in range(count):
                    if positions_a == positions_g == positions_p:
                        break
                    # 在最优解附近移动
                    if t == 0:
                        v = [0, 0]
                    else:
                        v = vel[i][index][k]
                    trans_position, v_iter = move(positions_a[0], positions_p[0], positions_g[0], c1 * r1, c2 * r2,
                                                  classroom_list, time_slot, w, v)
                    # 判断教室大小
                    if classroom_list[trans_position[0]][1] >= len(stus):
                        # 判断冲突
                        conflict = is_conflict(matrix_a, update_ST, update_TT, trans_position, start_week, end_week,
                                               week_type, students, teachers, stus, tea)
                        # 如果没有冲突
                        if not conflict:
                            # 删除矩阵a中删除原来的教学任务
                            # 矩阵a新的位置添加教学任务
                            matrix_a, update_ST, update_TT = update_timetable(matrix_a, update_ST, update_TT,
                                                                              positions_a[0], trans_position,
                                                                              start_week, end_week, week_type,
                                                                              students, teachers, stus, tea,
                                                                              task_num, classroom_list)
                            # 更新教学任务上课信息
                            update_task = update.updated_scheme(update_scheme[index], positions_a[0],
                                                                trans_position, classroom_list)
                            # 更新排课方案
                            update_scheme[index] = update_task
                    del positions_a[0]
                    del positions_g[0]
                    vel[i][index][k] = v_iter
            update_RTmatrix.append(matrix_a)
            update_schemeslist.append(update_scheme)
            update_STmatrix_filled.append(update_ST)
            update_TTmatrix_filled.append(update_TT)
        copy_schemesList = copy.deepcopy(update_schemeslist)
        RTmatrix_filled = copy.deepcopy(update_RTmatrix)
        STmatrix_filled = copy.deepcopy(update_STmatrix_filled)
        TTmatrix_filled = copy.deepcopy(update_TTmatrix_filled)
        # 重新计算适应度值
        fitness___ = Target.target_fun(update_RTmatrix, update_STmatrix_filled, update_TTmatrix_filled, buildings_dis,
                                       len(students), len(teachers))
        # 快速非支配排序、拥挤度计算
        newParetofrontiers = Super.super(fitness___)
        # 更新个体最优
        particle_p, RTparticle_p, fitness_p, s_t_p, t_t_p = update.update_pbest(copy_schemesList, update_RTmatrix,
                                                                                fitness___,
                                                                                update_STmatrix_filled,
                                                                                update_TTmatrix_filled,
                                                                                particle_p, RTparticle_p, fitness_p,
                                                                                s_t_p, t_t_p)
        # 更新全局最优(狮王位置)
        # particle_g, fitness_g, RTparticle_g = archiving.select_global_best(copy_schemesList, particle_g, fitness_g,
        #                                                                    newParetofrontiers, update_RTmatrix,
        #                                                                    RTparticle_g)
        particle_g, fitness_g, RTparticle_g = archiving.init_archive(copy_schemesList, newParetofrontiers,
                                                                     update_RTmatrix)
        print("迭代次数：", t + 1)
        print("学生教师方差：", fitness_g[1], "教室空闲率：", fitness_g[2], "平均距离：", fitness_g[3])
        x.append(t + 1)
        y1.append(fitness_g[1])
        y2.append(fitness_g[2])
        y3.append(fitness_g[3])
    return particle_g, fitness_g, x, y1, y2, y3, RTparticle_g


def find_all_element_positions(matrix, element, start_week):
    positions = []  # 存储所有值等于 element 的位置
    matrix_arr = np.array(matrix[start_week - 1], object)
    indices = np.array(np.where(matrix_arr != ""))
    for j in range(len(indices[0])):
        if matrix_arr[indices[0][j]][indices[1][j]][0] == element:
            positions.append(tuple(indices[:, j]))
    return positions


def is_conflict(matrix, update_ST, update_TT, position, start_week, end_week, week_type, students, teachers, stus, tea):
    # 冲突
    conflict = False
    room = position[0]
    time_plot = position[1]
    # 检查指定位置是否为空----添加周
    # 连续周
    if week_type == 0:
        for week in range(start_week, end_week + 1):
            # 教室时间冲突检测
            if matrix[week - 1][room][time_plot] != "":
                conflict = True
                break
            # 教师时间冲突
            if update_TT[week - 1][teachers.index(tea)][time_plot] != "":
                conflict = True
                break
            # 学生时间冲突
            for stu in stus:
                if update_ST[week - 1][students.index(stu)][time_plot] != "":
                    conflict = True
                    break
            if conflict:
                break
        return conflict
    # 单周
    if week_type == 1:
        for week in range(start_week, end_week + 1):
            if week % 2 == 1:
                # 教室时间冲突检测
                if matrix[week - 1][room][time_plot] != "":
                    conflict = True
                    break
                # 教师时间冲突
                if update_TT[week - 1][teachers.index(tea)][time_plot] != "":
                    conflict = True
                    break
                # 学生时间冲突
                for stu in stus:
                    if update_ST[week - 1][students.index(stu)][time_plot] != "":
                        conflict = True
                        break
                if conflict:
                    break
        return conflict
    # 双周
    if week_type == 2:
        for week in range(start_week, end_week + 1):
            if week % 2 == 0:
                # 教室时间冲突检测
                if matrix[week - 1][room][time_plot] != "":
                    conflict = True
                    break
                # 教师时间冲突
                if update_TT[week - 1][teachers.index(tea)][time_plot] != "":
                    conflict = True
                    break
                # 学生时间冲突
                for stu in stus:
                    if update_ST[week - 1][students.index(stu)][time_plot] != "":
                        conflict = True
                        break
                if conflict:
                    break
        return conflict


def update_timetable(matrix, update_ST, update_TT, position_a, position_b, start_week, end_week, week_type, students,
                     teachers, stus, tea, task_num, classroom_list):
    # 更新时间表
    if week_type == 0:
        for i in range(start_week, end_week + 1):
            matrix[i - 1][position_a[0]][position_a[1]] = ""
            matrix[i - 1][position_b[0]][position_b[1]] = (task_num, classroom_list[position_b[0]][1], len(stus))
            update_TT[i - 1][teachers.index(tea)][position_a[1]] = ""
            update_TT[i - 1][teachers.index(tea)][position_b[1]] = (task_num, classroom_list[position_b[0]])
            for stu in stus:
                update_ST[i - 1][students.index(stu)][position_a[1]] = ""
                update_ST[i - 1][students.index(stu)][position_b[1]] = (task_num, classroom_list[position_b[0]])
    if week_type == 1:
        for i in range(start_week, end_week + 1):
            if week_type % 2 == 1:
                matrix[i - 1][position_a[0]][position_a[1]] = ""
                matrix[i - 1][position_b[0]][position_b[1]] = (task_num, classroom_list[position_b[0]][1], len(stus))
                update_TT[i - 1][teachers.index(tea)][position_a[1]] = ""
                update_TT[i - 1][teachers.index(tea)][position_b[1]] = (task_num, classroom_list[position_b[0]])
                for stu in stus:
                    update_ST[i - 1][students.index(stu)][position_a[1]] = ""
                    update_ST[i - 1][students.index(stu)][position_b[1]] = (task_num, classroom_list[position_b[0]])
    if week_type == 2:
        for i in range(start_week, end_week + 1):
            if week_type % 2 == 0:
                matrix[i - 1][position_a[0]][position_a[1]] = ""
                matrix[i - 1][position_b[0]][position_b[1]] = (task_num, classroom_list[position_b[0]][1], len(stus))
                update_TT[i - 1][teachers.index(tea)][position_a[1]] = ""
                update_TT[i - 1][teachers.index(tea)][position_b[1]] = (task_num, classroom_list[position_b[0]])
                for stu in stus:
                    update_ST[i - 1][students.index(stu)][position_a[1]] = ""
                    update_ST[i - 1][students.index(stu)][position_b[1]] = (task_num, classroom_list[position_b[0]])
    return matrix, update_ST, update_TT


# #初始化个体最优值
def init_pbest(init_population, init_popuRT, fitness_, int_ss, int_ts):  # 第一次迭代最优个体就是本身
    return init_population, init_popuRT, fitness_, int_ss, int_ts


# 移动位置
def move(position_a, position_p, position_g, A1, A2, classroom_list, time_slot, w, v):
    position_a = np.array(position_a)
    position_p = np.array(position_p)
    position_g = np.array(position_g)
    v = np.array(v)
    v_iter = w * v + A1 * (position_p - position_a) + A2 * (position_g - position_a)
    if np.abs(v_iter[0]) > 0.5 * len(classroom_list):
        if v_iter[0] > 0:
            v_iter[0] = np.abs(v_iter[0]) % (0.5 * len(classroom_list))
        else:
            v_iter[0] = -(np.abs(v_iter[0]) % (0.5 * len(classroom_list)))
    if np.abs(v_iter[1]) > 0.5 * time_slot:
        if v_iter[1] > 0:
            v_iter[1] = np.abs(v_iter[1]) % (0.5 * time_slot)
        else:
            v_iter[1] = -(np.abs(v_iter[1]) % (0.5 * time_slot))
    trans_position = np.round(position_a + v_iter)
    # print(v_iter)
    if (trans_position[0] < 0 and trans_position[1] < 0) or (
            trans_position[0] >= len(classroom_list) and trans_position[1] >= time_slot) or (
            trans_position[0] < 0 and trans_position[1] >= time_slot) or (
            trans_position[0] >= len(classroom_list) and trans_position[1] < 0):
        trans_position = position_a
        return tuple(map(int, trans_position)), v_iter
    if (trans_position[0] < 0 and 0 <= trans_position[1] < time_slot) or (
            trans_position[0] >= len(classroom_list) and 0 <= trans_position[1] < time_slot):
        trans_position = (position_a[0], trans_position[1])
        return tuple(map(int, trans_position)), v_iter
    if (0 <= trans_position[0] < len(classroom_list) and trans_position[1] < 0) or (
            0 <= trans_position[0] < len(classroom_list) and trans_position[1] >= time_slot):
        trans_position = (trans_position[0], position_a[1])
        return tuple(map(int, trans_position)), v_iter

    return tuple(map(int, trans_position)), v_iter
